﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

namespace Brjnk.MVVM
{
    /// <summary>
    /// ObservableCollection rozšířená o události pro přidání a odebrání položek.
    /// ObservableCollection totiž nepředává v události CollectionChanged seznam odstraněných objektů při volání Clear()
    /// </summary>
    public class ObservableCollectionEx<T> : ObservableCollection<T>, IAddRemoveEvents
    {
        public ObservableCollectionEx() { }

        public event EventHandler<ItemsEventArgs<T>> ItemsAdded;

        public event EventHandler<ItemsEventArgs<T>> ItemsRemoved;

        protected override void ClearItems()
        {
            T[] items = null;
            if (ItemsRemoved != null || ItemsRemovedUntyped != null)
                items = this.ToArray();

            base.ClearItems();

            OnItemsRemoved(items);
        }

        private void OnItemsRemoved(T[] items)
        {
            if (ItemsRemoved != null) ItemsRemoved(this, new ItemsEventArgs<T>(items));
            if (ItemsRemovedUntyped != null) ItemsRemovedUntyped(this, new ItemsEventArgs(items));
        }

        private void OnItemRemoved(T item)
        {
            if (ItemsRemoved != null) ItemsRemoved(this, new ItemsEventArgs<T>(new T[] { item }));
            if (ItemsRemovedUntyped != null) ItemsRemovedUntyped(this, new ItemsEventArgs(new object[] { item }));
        }

        private void OnItemAdded(T item)
        {
            if (ItemsAdded != null) ItemsAdded(this, new ItemsEventArgs<T>(new T[] { item }));
            if (ItemsAddedUntyped != null) ItemsAddedUntyped(this, new ItemsEventArgs(new object[] { item }));
        }

        protected override void RemoveItem(int index)
        {
            var item = this[index];
            base.RemoveItem(index);
            OnItemRemoved(item);
        }

        protected override void SetItem(int index, T item)
        {
            var removedItem = this[index];
            base.SetItem(index, item);
            var addedItem = item;
            OnItemRemoved(removedItem);
            OnItemAdded(addedItem);
        }

        protected override void MoveItem(int oldIndex, int newIndex)
        {
            base.MoveItem(oldIndex, newIndex);
        }

        protected override void InsertItem(int index, T item)
        {
            base.InsertItem(index, item);
            OnItemAdded(item);
        }

        public event EventHandler<ItemsEventArgs> ItemsAddedUntyped;

        public event EventHandler<ItemsEventArgs> ItemsRemovedUntyped;
    }

    public interface IAddRemoveEvents : IEnumerable
    {
        event EventHandler<ItemsEventArgs> ItemsAddedUntyped;

        event EventHandler<ItemsEventArgs> ItemsRemovedUntyped;
    }

    public class ItemsEventArgs<T> : EventArgs
    {
        private IEnumerable<T> items;

        public ItemsEventArgs(IEnumerable<T> items)
        {
            this.items = items;
        }

        public IEnumerable<T> Items
        {
            get { return items; }
        }
    }

    public class ItemsEventArgs : EventArgs
    {
        private IEnumerable items;

        public ItemsEventArgs(IEnumerable items)
        {
            this.items = items;
        }

        public IEnumerable Items
        {
            get { return items; }
        }
    }
}